This vignette assumes a SQL server at localhost (we use PostgreSQL), with data in OMOP Common Data Model v5.4 format in schema cdm_new_york3. The patient records shown in this example are synthetic data from Synthea(TM) Patient Generator.

library(phea)
library(dplyr)

# Connect to SQL server.
dbcon <- DBI::dbConnect(RPostgres::Postgres(),
  host = 'localhost', port = 7654, dbname = 'fort',
  user = cred$pg$user, password = cred$pg$pass)

# Call setup_phea so we can use sqlt() and sql0().
setup_phea(dbcon, 'cdm_new_york3')

In this vignette we identify:

Case A.

Case B.

Here’s how we compute it:

In both cases, we will normalize the unit of measurement of SCr to md/dL prior to computing formulas. We could also use formulas to convert the units, and the result would be the same.

Create components

Serum creatinine

We collect SCr records from MEASUREMENT and convert the units to mg/dL.

# 3051825 38483-4 Creatinine [Mass/volume] in Blood
# 3016723 2160-0 Creatinine [Mass/volume] in Serum or Plasma

# "A" records: Unit is 'mg/dL'
scr_records_a <- sqlt(measurement) |>
  filter(measurement_concept_id %in% c(3051825, 3016723) &&
    unit_source_value == 'mg/dL')

# "B" records: Unit is 'µmol/L', but we convert to 'mg/dL'
scr_records_b <- sqlt(measurement) |>
  filter(measurement_concept_id %in% c(3051825, 3016723) &&
    unit_source_value == 'µmol/L') |>
  mutate( # Convert µmol/L to mg/dL
    value_as_number = value_as_number / 88.42,
    unit_source_value = 'mg/dL')

# Combine all available serum creatinine records into a single record source.
scr_record_source <- union_all(scr_records_a, scr_records_b) |>
  make_record_source(
    ts = measurement_datetime,
    pid = person_id)

Glomerular filtration rate

We collect GFR records from MEASUREMENT.

## GFR
# 46236952 77147-7 Glomerular filtration rate/1.73 sq M.predicted [Volume Rate/Area] in Serum, Plasma or Blood by Creatinine-based formula (MDRD)
gfr_record_source <- sqlt(measurement) |>
  filter(measurement_concept_id == 46236952) |>
  make_record_source(
    ts = measurement_datetime,
    pid = person_id)

Calculate the phenotype

Formula scr_03_criteria contains the logic of case A (difference >= 0.3 mg/dL), while scr_15_criteria contains case B (ratio >= 1.5).

scr_change <- calculate_formula(
  components = list(
    # Current SCr
    scr = make_component(scr_record_source),
    
    # Minimum value within 48-hour window
    scr_48h_min = make_component(scr_record_source,
      window = '48 days', .delay_fn = 'min'),
    
    # Minimum value within 7-day window
    scr_7d_min = make_component(scr_record_source,
      window = '7 months', .delay_fn = 'min'),
    
    # Current glomerular filtration rate (GFR)
    gfr = make_component(gfr_record_source),
    
    # Glomerular filtration rate 3 to 5 months older than phenotype date
    gfr_3mo = make_component(gfr_record_source,
      delay = '3 months', window = '5 months')),
    
  fml = list(
    scr_03_criteria = 'scr_value_as_number - scr_48h_min_value_as_number >= 0.3',
    
    scr_15_criteria = 'scr_value_as_number / scr_7d_min_value_as_number >= 1.5',
    
    gfr_criteria = 'gfr_value_as_number < 60 AND gfr_3mo_value_as_number < 60'),
  
  export = c(
    'scr_measurement_datetime',
    'scr_48h_min_measurement_datetime',
    'scr_7d_min_measurement_datetime',
    'gfr_measurement_datetime',
    'gfr_3mo_measurement_datetime')
)

Plot the phenotype for a random patient

scr_change |>
  select(-ends_with('datetime')) |>
  phea_plot(22444)
#> Collecting lazy table, done.

Obtain the SQL query that computes the phenotype

To see the SQL query underlying the phenotype, use helper function code_shot(), or dbplyr::sql_render(), or the .clip_sql option in calculate_formula().